package com.tangsm.spring.boot.deferred.result.controller;

import com.tangsm.spring.boot.deferred.result.domain.vo.Result;
import com.tangsm.spring.boot.deferred.result.service.TaskService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;

import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * DeferredResult + 线程池实现异步请求调用
 * <pre>
 * DeferredResult和Callable一样，DeferredResult也是为了支持异步调用。
 * 两者的主要差异，Sunny觉得主要在DeferredResult需要自己用线程来处理结果setResult，而Callable的话不需要我们来维护一个结果处理线程。
 * 总体来说，Callable的话更为简单，同样的也是因为简单，灵活性不够；
 * 相对地，DeferredResult更为复杂一些，但是又极大的灵活性。在可以用Callable的时候，直接用Callable；
 * 而遇到Callable没法解决的场景的时候，可以尝试使用DeferredResult。这里Sunny将会设计两个DeferredResult使用场景。
 *
 * 线程池保持队列+DeferredResult配置结果信息与委托事件
 * </pre>
 *
 * @author tangsm
 */
@RestController
@RequestMapping("/deferred/thread")
public class DeferredResultThreadTaskController {
    private static final Logger log = LoggerFactory.getLogger(DeferredResultThreadTaskController.class);

    /**
     * 随机数
     */
    private static final Random random = new Random();

    /**
     * 线程池
     */
    private final ExecutorService threadPool = Executors.newFixedThreadPool(10);

    @Autowired
    private TaskService taskService;

    /**
     * 异步获取
     *
     * @return 响应数据
     */
    @GetMapping(value = "/get")
    public DeferredResult<Result<String>> getResult() {
        log.info("主线程名称：" + Thread.currentThread().getName());

        // 设置超时时间20秒
        DeferredResult<Result<String>> result = new DeferredResult<>(5000L);

        // 异步请求超时时调用
        result.onTimeout(() -> {
            log.error("请求超时");
            result.setResult(new Result<>(408, "请求超时", null));
        });

        // 以便在异步请求完成时调用
        result.onCompletion(() -> log.info("异步调用结束"));

        // 在池化线程中或在调用线程中执行
        threadPool.execute(() -> {
            log.info("新线程名称：" + Thread.currentThread().getName());
            try {
                // 睡眠10秒以内时间
                int timeout = random.nextInt(5);
                log.info("睡眠{}秒时间", timeout);

                TimeUnit.SECONDS.sleep(timeout);
            } catch (Exception e) {
                log.error("异步线程发生异常");
                e.printStackTrace();
            }

            // 设置DeferredResult的值并处理
            result.setResult(taskService.getResult());
        });

        return result;
    }
}
